<html>
<!-- // Начиная с версии 2.9, разработку ведет Дмитрий Евдокимов, diev at mail.ru -->
<head>
<meta http-equiv=content-type content="text-html; charset=utf-8">
<meta http-equiv=MSThemeCompatible content=Yes>
<hta:application
    id="SVKTrans"
    applicationName="SVK Transport"
    icon="mnmsrvc.exe"
    maximizeButton="no"
    innerBorder="no"
    scroll="no"
    selection="no"
    contextMenu="no"
    singleinstance="yes"
    version="2.12.0"
    author="mozers™ (retired)"
    email="mozers@mail.ru"
    icq="256106175"
    coauthor="Dmitrii Evdokimov, diev@mail.ru (active)"
 />
<style type="text/css">
    body, table {font-family:MS Sans Serif; font-size:8pt; background-color:threedface; overflow:hidden;}
    body {margin:0;}
    button {font-weight:bold; color:highlight; font-size:8pt;}
    textarea {font-family:MS Sans Serif; font-size:8pt;}
    td {margin:0; white-space:nowrap;}
    label, legend {color:highlight;}
    legend {font-weight:bold;}
    input#TimerVisible {font-family:Courier; font-weight:bold; font-size:12pt; width:50pt; text-align:center; color:blue;}
    input#TimerHidden {display:none;}
</style>
<script type="text/javascript">
var app_name = SVKTrans.applicationName;
document.title = app_name + ' - ' + SVKTrans.version;
window.resizeTo(550, 360); // начальный размер окна приложения (выставляйте по вкусу)

var ini_filename = "SVKTrans.ini";      // имя ini файла
var timeout = [];                       // массив для хранения интервалов таймеров
var num_timer_onfinish;                 // номер таймера, вызвавшего срабатывание
var current_day = new Date().getDate(); // текущий день (используется для очистки окна лога)
var online = false;                     // состояние соединения
var StatInfoInFilters = [];             // массив с каталогами назначения и соответствующими им масками файлов
                                        // для сортировки входящих информационных сообщений

// Объекты ActiveX
var WshShell;
var objExec;
var FSO;
var adoStream;
var xmlDoc;
var httpRequest;
ActiveX_Init();

var script_path = unescape(document.URL).replace(/^file:\/\/(.+?)[^\\]+$/, '$1'); // папка данного скрипта

var store_path = script_path + "logs\\"; // каталог для сохранения log файлов
var rest_file = script_path + "restrictions.dat"; // файл с периодами простоя скрипта
var Arr_Restrictions = []; // массив со всеми датами/временем когда периодическая проверка отключена

var INI = ReadIniFile(script_path + ini_filename);
var url_get = 'http://' + INI.Account.svk_host + '/KbrInterface/System/Main.aspx';
var url_get_list = url_get + '?Method=ExtendedList';
var url_post = 'http://' + INI.Account.svk_host + '/KbrReceive/';
var web_login;
var web_passw;

LoadStatInfoInFilters();
MakeAllDir();

var objSocket;
var objXBuffer;
InitSocket();
InitXBuffer();

var myPID = GetMyPID();  // ProcessId нашего приложения
//=====================================================
//  C O M M O N
//=====================================================

// Возвращает ProcessId нашего приложения
function GetMyPID() {
    var pid = new ActiveXObject('WScript.Shell').Exec('rundll32 kernel32,Sleep').ProcessID;
    var oProc = new Enumerator(GetObject('winmgmts:\\\\.\\root\\cimv2').ExecQuery('select * from Win32_Process where ProcessId='+pid)).item();
    var parent_id = oProc.ParentProcessId;
    oProc.Terminate();
    return parent_id;
}

// Перезапуск приложения
function Restart() {
    var rst_file = WshShell.ExpandEnvironmentStrings('%TEMP%\\svktr_restart.cmd');
    var this_script = unescape(document.URL).replace(/^file:\/\//, '');
    var rst_text = '@echo off\nCHCP 1251>nul\n';
    rst_text += 'ping -n 3 -w 1000 127.0.0.1 > nul\n';
    rst_text += 'taskkill /f /pid ' + myPID + '\n';
    rst_text += 'ping -n 2 -w 1000 127.0.0.1 > nul\n';
    rst_text += 'start "" "' + this_script + '"';
    WriteTextFile(rst_file, rst_text);
    WshShell.Run(rst_file, 0, true);
}

// Отлов необрабатываемых ошибок
function catchError(err_text, err_src, err_line) {
    var msg_text = '!ОШИБКА СКРИПТА\nURL:\t'+err_src+'\nLine:\t'+err_line+'\nError:\t'+err_text;
    WriteLog(msg_text);
    WriteLog('Пытаюсь продолжить...');
    return true;
}
window.onerror = catchError;

// Проверка содержится значение в массиве или нет
function isContainArray(arr, value) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] == value) {
            return true;
        }
    }
    return false;
}

// Чтение текстового файла
function ReadTextFile(filename) {
    var text = '';
    if (FSO.FileExists(filename)) {
        if (FSO.GetFile(filename).Size > 0) {
            var tfile = FSO.OpenTextFile(filename);
            text = tfile.ReadAll();
            tfile.Close();
        }
    }
    return text;
}

// Запись текста в файл
function WriteTextFile(filename, text) {
    var tfile = FSO.OpenTextFile(filename, 2, true);
    tfile.Write(text);
    tfile.Close();
}

// Читает файл в массив из сегментов заданного размера
function ReadFileSegments(filename) {
    if (adoStream.State !== 0) {
        adoStream.Close(); // Check adStateClosed
    }
    adoStream.Type = 1; //adTypeBinary
    adoStream.Open();
    adoStream.LoadFromFile(filename);
    var data = [], i = 0, d;
    do {
        d = INI.Config.segment_size_upload ?
            adoStream.Read(INI.Config.segment_size_upload) :
            adoStream.Read();
        if (d === null) {
            adoStream.Close();
            return data;
        } else {
            data[i] = d;
            i++;
        }
    } while (true);
}

// Возвращает MD5 файла в HEX виде, закодированный в base64
// (используется ActiveX "XStandard.Buffer" и VB ф-ция Chr)
function MD5(fname) {
    objXBuffer.Reset();
    objXBuffer.Load (fname);
    var strHex = objXBuffer.MD5;
    objXBuffer.Reset();
    for (var i = 0; i < strHex.length; i += 2) {
        var ch = ASCIIChar(Number('0x' + strHex.substr(i, 2)));
        objXBuffer.Write(ch);
    }
    return objXBuffer.Base64String;
}

// Кодирование в base64
function utf8base64(str) {
    function utf8(str) {
        if (adoStream.State !== 0) {
            adoStream.Close(); // Check adStateClosed
        }
        adoStream.Type = 2; // adTypeText
        adoStream.Charset = 'utf-8';
        adoStream.Open();
        adoStream.WriteText(str);
        adoStream.Position = 0;
        adoStream.Type = 1; // adTypeBinary
        adoStream.Read(3); // BOM нам не нужен
        var data = adoStream.Read();
        adoStream.Close();
        return data;
    }
    var xml = xmlDoc.createElement('tmp');
    xml.dataType = 'bin.base64';
    xml.nodeTypedValue = utf8(str);
    return xml.text;
}

// Возвращает строку, состоящую из num символов chr
function Space(chr, num) {
    return new Array(num + 1).join(chr);
}

// Генерация GUID
function GUID() {
    var result = '';
    for (var j = 0; j < 32; j++) {
        if (j == 8 || j == 12 || j == 16 || j == 20) {
            result += '-';
        }
        result += Math.floor(Math.random() * 16).toString(16);
    }
    return result;
}

// Чтение ini файла в объект со свойствами идентичными именам ключей
function ReadIniFile(ini_file) {
    var oINI = {};
    if ((FSO.FileExists(ini_file)) && (FSO.GetFile(ini_file).Size > 0)) {
        var tfile = FSO.OpenTextFile(ini_file);
        var text = tfile.ReadAll();
        tfile.Close();
        var arr_lines = text.match(/[^\r\n]+/g);
        var section, param, value;
        for(var i = 0, line; i < arr_lines.length; i++) {
            line = arr_lines[i].replace(/^\s*/, '').replace(/\s*$/, '');
            if (!/^[;#]/.test(line)) {        // comment
                if (/^\[(.+)\]/.test(line)) { // section
                    section = RegExp.$1;
                    if ((section) && (!oINI[section])) {
                        oINI[section] = {};
                    }
                } else if (/^(.+?)\s*=\s*(.+)/.test(line)) { // param=value
                    param = RegExp.$1;
                    value = RegExp.$2;
                    if (param && value) {
                        oINI[section][param] = value;
                    }
                }
            }
        }
    }
    return oINI;
}

// Преобразовывает заданную дату в объект со свойствами: year, month, day, hours, min, sec
function objDate(obj) {
    function format(x) {
        return x < 10 ?
            '0' + x :
             String(x);
    }
    var d = obj ?
        new Date(obj) :
        new Date();
    var y = String(d.getYear());
    return {
        year:  y,                        // yyyy
        yy:    y.replace(/^../, ''),     // yy
        month: format(d.getMonth() + 1), // mm
        day:   format(d.getDate()),      // dd
        hours: format(d.getHours()),     // hh
        min:   format(d.getMinutes()),   // mm
        sec:   format(d.getSeconds())    // ss
    };
}

// Переводит кол-во секунд во время в виде "mm:ss"
function sec2time(sec) {
    function format(x) {
        return x < 10 ?
            '0' + x :
            x;
    }
    var m = format(Math.floor(sec / 60));
    var s = format(sec % 60);
    return m + ':' + s;
}

// Создание каталога (при ошибке закрывает приложение)
function CreateFolder(path) {
    if (!FSO.FolderExists(path)) {
        var ret = WshShell.Run('%COMSPEC% /c MD "' + path + '"', 0, true);
        if (ret !== 0) {
            WriteLog('!Ошибка при создании каталога "' + path + '"');
            self.close();
        }
    }
}

// Создание всех каталогов приема/выгрузки сообщений
function MakeAllDir() {
    var path;
    CreateFolder(store_path);
    for (var i in INI.Paths) {
        path = (/^stat_info_in_filter\d$/.test(i)) ?
            INI.Paths[i].replace(/(.+)\\.+/, '$1') :
                (/^stat_info_out_filter\d$/.test(i)) ?
                INI.Paths[i].replace(/(.+)\\.+/, '$1') :
                INI.Paths[i];
        if ((path) && (!FSO.FolderExists(path))) {
            if (WshShell.Popup('Заданный Вами в SVKTrans.ini (' + i + ') каталог\n' + path + '\nне существует!\n\nСоздать его?', 0,
                'Предупреждение', 36) == 6) {
                CreateFolder(path);
            } else {
                self.close();
            }
        }
    }
}

// Копирование/перемещение файла (К существующему в каталоге назначения добавляется расширение с датой его последней модификации)
function XCopyFile(filename, destination, del_source) {
    var dest_file = destination + '\\' + FSO.GetFile(filename).Name;
    if (FSO.FileExists(dest_file)) {
        var File = FSO.GetFile(dest_file);
        var dt = objDate(File.DateLastModified);
        var old_file = File.Name + '.' + dt.year+dt.month+dt.day+dt.hours+dt.min+dt.sec;
        WriteLog("! в каталоге " + destination + " уже имеется файл с таким именем.\n           Переименовываю его в " + old_file);
        FSO.MoveFile(dest_file, destination + '\\' + old_file);
    }
    FSO.CopyFile(filename, destination);
    if (del_source) {
        FSO.DeleteFile(filename, true);
    }
}

// Бекап получаемых|сохраняемых файлов (direction = IN|OUT)
function Backup(filename, direction) {
    var dt = objDate();
    var backup_dir = INI.Paths.backup + '\\' + direction + '\\' + dt.year + '\\' + dt.month + '\\' + dt.day;
    if (!FSO.FolderExists(backup_dir)) {
        CreateFolder(backup_dir);
    }
    XCopyFile(filename, backup_dir + '\\');
}

// Проверка наличия такого файла в бекапе IN за день
function BackupedIN(filename) {
    var dt = objDate();
    var backup_dir = INI.Paths.backup + '\\IN\\' + dt.year + '\\' + dt.month + '\\' + dt.day;
    return FSO.FileExists(backup_dir + '\\' + filename);
}

// Запись лога в textarea и в файл (Предупреждения отправляются администратору)
function WriteLog(message) {
    var dt = objDate();
    var log_message = '[' + dt.hours + ':' + dt.min + '.' + dt.sec + '] ' + message + '\r\n';
    if (/^ /.test(message)) { // Если сообщение начинается с пробела, то добавляем пустую строку перед ним
        log_message = '\r\n' + log_message;
    }

    if (dt.day != current_day) { // Очищаем textarea idLog первым сообщением нового дня
        idLog.value = '';
        current_day = dt.day;
    }
    idLog.insertAdjacentText('BeforeEnd', log_message);
    idLog.scrollTop = idLog.scrollHeight;

    var logfilename = store_path + dt.year + dt.month + dt.day + ".log";
    var tfile = FSO.OpenTextFile(logfilename, 8, true);
    tfile.Write(log_message);
    tfile.Close();
    // Если сообщение начинается с восклицательного знака, то посылаем net send администраторам
    if ((/^\s*!/.test(message)) && (INI.Account.messages_recipient)) {
        var recipients = INI.Account.messages_recipient.match(/[^,;]+/g);
        for (var i = 0; i < recipients.length; i++) {
            WshShell.Run('net send ' + recipients[i] + ' ' + message, 6, false);
        }
    }
}

// Прокручиваем лог по таймеру
function ScrollTopHeight() {
    idLog.scrollTop = idLog.scrollHeight;
}

// Читаем лог из файла в textarea (при старте приложения)
function ReadLog() {
    var dt = objDate();
    var logfilename = store_path + dt.year + dt.month + dt.day + ".log";
    if (FSO.FileExists(logfilename)) {
        if (FSO.GetFile(logfilename).Size > 0) {
            var tfile = FSO.OpenTextFile(logfilename);
            var text = tfile.ReadAll();
            tfile.Close();
            idLog.insertAdjacentText('BeforeEnd', text);
            setTimeout(ScrollTopHeight, 1000);
        }
    }
}

// Перекодировщик текста dos866 -> win1251
function dos2win(text_in) {
    if (adoStream.State !== 0) {
        adoStream.Close(); // Check adStateClosed
    }
    adoStream.Open();
    adoStream.Type = 2;
    adoStream.Charset = 'windows-1251';
    adoStream.WriteText(text_in);
    adoStream.Flush();
    adoStream.Position = 0;
    adoStream.Charset = 'cp866';
    var text_out = adoStream.ReadText(-1);
    adoStream.Close();
    return text_out;
}

// Соединение с telnet-сервером
function TelnetConnect() {
    objSocket.DoTelnetEmulation = true;
    objSocket.TelnetEmulation = "TTY";
    objSocket.TimeOut = 10000; // время ожидания ответа сервера (мсек)
    objSocket.Host = INI.Account.telnet_host;
    var try_count = 4; // число попыток подключения
    var ret = false;

    do {
        try {
            objSocket.Open();
            objSocket.WaitFor("Username:");
            objSocket.SendLine(INI.Account.telnet_login);
            objSocket.WaitFor("Password:");
            objSocket.SendLine(INI.Account.telnet_passw);
            ret = objSocket.WaitFor("Authentication Successful");
        } catch(e) {
        }
        try {
            objSocket.Close();
        }
        catch(e) {
        }
        try_count--;
    } while ((!ret) && (try_count !== 0));
    return ret;
}

//=====================================================
//  T I M E R   C O N T R O L
//=====================================================

// Сброс таймера
function InitTimer(num) {
    TimerHidden[num].value = timeout[num];
    TimerVisible[num].value = sec2time(TimerHidden[num].value);
    TimerVisible[num].disabled = true;
}

// Сброс всех таймеров
function InitAllTimers() {
    for (var i = 0; i < TimerVisible.length; i++) {
        InitTimer(i);
    }
}

// Разрешаем таймеру уменьшать оставшееся время
function StartTimer(num) {
    if ((num === 0) && (ChkRestrictions.checked) && (TextRestrict.style.color == "red")) {
        return;
    }
    TimerVisible[num].disabled = false;
}

// Каждую секунду уменьшает на 1 показания всех незадизабленых таймеров и, если значение одного из них подошло к 0, запускает соответствующую таймеру функцию
function ShowTimers() {
    CheckRestrictions();
    for (var num = 0; num < TimerHidden.length; num++) {
        if (!TimerVisible[num].disabled) {
            TimerHidden[num].value--;
            TimerVisible[num].value = sec2time(TimerHidden[num].value);
            if (TimerHidden[num].value == '0') {
                // Один из таймеров досчитал до 0
                InitTimer(num);
                num_timer_onfinish = num;
                if (num == 1) { // Отправка исходящих сообщений;
                    if (CheckOutbox()) Connect();
                    StartTimer(1); // проверка наличия исходящих сообщений
                } else {
                    Connect();
                }
            }
        }
    }
    setTimeout(ShowTimers, 1000);
}

// Подключение
function Connect() {
    function EventDescription() {
        switch(num_timer_onfinish) {
            case 0: return ' > Проверка по таймауту';
            case 1: return ' > Отправка исходящих сообщений';
            case 2: return ' > Запрос ответа';
            case 3: return ' > Повтор неудавшегося подключения';
            case 4: return ' > Запуск вручную';
            case 5: return ' > Запуск по окончанию ограничения';
            case 6: return ' > Запуск при старте приложения';
        }
    }
    online = false;
    InitAllTimers();
    WriteLog(EventDescription());
    var connection = INI.Account.connection; // Имя dialup соединения с СВК 
    if (connection) { // дозвон осуществляется только при наличии заданного имени dialup соединения
        var chan_login = INI.Account.chan_login; // Логин канальной аутентификации
        var chan_passw = INI.Account.chan_passw; // Пароль канальной аутентификации
        var ras_log = store_path + 'ras.log';
        // WriteLog('Установка соединения ' + connection + '...');
        var ret = 1;
        try {
            ret = WshShell.Run('%COMSPEC% /c RASDIAL '+connection+' '+chan_login+' '+chan_passw+' > '+ras_log + ' 2<&1', 0, true);
            var output=ReadTextFile(ras_log);
            output = dos2win(output);
            output = output.replace(/^(.)/gm, '\t$1').replace(/\r\n$/g, '');
            WriteLog(output);
        } catch(e) {
            WriteLog('!Ошибка при вызове RASDIAL');
        }
        if (ret !== 0) {
            StartTimer(3); // повтор неудавшегося подключения
            return;
        }
    }
    // WriteLog('Соединение установлено');
    if (INI.Account.telnet_host) {
        if (!TelnetConnect()) {
            WriteLog('!Ошибка telnet аутентификации');
            StartTimer(3); // повтор неудавшегося подключения
            return;
        } else {
            WriteLog('Telnet аутентификация пройдена');
        }
    }
    online = true;

    /*
    // первая очередь (платежи)
    Upload(1);
    Download(1);

    // вторая очередь (отчетность)
    Upload(2);
    Download(2);
    */

    Upload();
    Download();

    StartTimer(0); // периодическая проверка
    StartTimer(1); // проверка наличия исходящих сообщений
}

// Проверка наличия исходящих сообщений
function CheckOutbox() {
    /*
    if ((INI.Paths.pay_out) &&
        (FSO.FolderExists(INI.Paths.pay_out)) &&
        (FSO.GetFolder(INI.Paths.pay_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.pay_test_out) &&
        (FSO.FolderExists(INI.Paths.pay_test_out)) &&
        (FSO.GetFolder(INI.Paths.pay_test_out).Files.Count > 0)) {
            return true;
        }
    */
    if ((INI.Paths.stat_info_out) &&
        (FSO.FolderExists(INI.Paths.stat_info_out)) &&
        (FSO.GetFolder(INI.Paths.stat_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.expl_info_out) &&
        (FSO.FolderExists(INI.Paths.expl_info_out)) &&
        (FSO.GetFolder(INI.Paths.expl_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.lic_info_out) &&
        (FSO.FolderExists(INI.Paths.lic_info_out)) &&
        (FSO.GetFolder(INI.Paths.lic_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.otzi_info_out) &&
        (FSO.FolderExists(INI.Paths.otzi_info_out)) &&
        (FSO.GetFolder(INI.Paths.otzi_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.sopr_info_out) &&
        (FSO.FolderExists(INI.Paths.sopr_info_out)) &&
        (FSO.GetFolder(INI.Paths.sopr_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.ubn_info_out) &&
        (FSO.FolderExists(INI.Paths.ubn_info_out)) &&
        (FSO.GetFolder(INI.Paths.ubn_info_out).Files.Count > 0)) {
            return true;
        }
    if ((INI.Paths.upsir_info_out) &&
        (FSO.FolderExists(INI.Paths.upsir_info_out)) &&
        (FSO.GetFolder(INI.Paths.upsir_info_out).Files.Count > 0)) {
            return true;
        }
    return false;
}

// Проверка ограничений на дату/время
function CheckRestrictions() {
    function GetRestrictions() {
        if (Arr_Restrictions.length > 0) {
            var d = new Date();
            for (var i = 0; i < Arr_Restrictions.length; i++) {
                // День недели: Пн, Вт, Ср, Чт, Пт, Сб, Вс
                var re = new RegExp(Arr_Restrictions[i], 'i');
                if (re.test('ВсПнВтСрЧтПтСб')) {
                    if (d.getDay() == RegExp.lastIndex / 2 - 1) {
                        return Arr_Restrictions[i];
                    }
                }
                // Дневной интервал вида 17.04.2010-18.04.2010
                if (/(\d\d).(\d\d).(\d\d\d\d)\-(\d\d).(\d\d).(\d\d\d\d)/.test(Arr_Restrictions[i])) {
                    var date_start = new Date(RegExp.$3, RegExp.$2 - 1, RegExp.$1);
                    var date_end = new Date(RegExp.$6, RegExp.$5 - 1, RegExp.$4);
                    if ((d >= date_start) && (d <= date_end)) {
                        return Arr_Restrictions[i];
                    }
                }
                // Дата вида 01.05.2010
                if (/(\d\d).(\d\d).(\d\d\d\d)/.test(Arr_Restrictions[i])) {
                    if ((d.getFullYear() == RegExp.$3) &&
                        (d.getMonth() == RegExp.$2 - 1) &&
                        (d.getDate() == RegExp.$1)) {
                        return Arr_Restrictions[i];
                    }
                }
                // Временной интервал вида 00:00-08:30
                if (/(\d\d):(\d\d)\-(\d\d):(\d\d)/.test(Arr_Restrictions[i])) {
                    var time_start = Number(RegExp.$1) * 60 + Number(RegExp.$2);
                    var time_end = Number(RegExp.$3) * 60 + Number(RegExp.$4);
                    var time_current = d.getHours() * 60 + d.getMinutes();
                    if ((time_current >= time_start) && (time_current <= time_end)) {
                        return Arr_Restrictions[i];
                    }
                }
            }
        }
        return '';
    }

    var current_restriction = GetRestrictions();
    if (current_restriction === '') {
        if (TextRestrict.style.color == "red") {
            TextRestrict.style.color = "highlight";
            TextRestrict.innerText = "нет";
            num_timer_onfinish = 5;
            Connect();
        }
        TextRestrict.style.color = "highlight";
        TextRestrict.innerText = "нет";
    } else {
        if (TextRestrict.style.color != "red") {
            if (ChkRestrictions.checked) InitTimer(0);
        }
        TextRestrict.style.color = "red";
        TextRestrict.innerText = current_restriction;
    }
}

//=====================================================
//  D O W N L O A D
//=====================================================

// Запрос на загрузку данных с заданного URL (последовательность для IE: open, setRequestHeader, onreadystatechange, send)
// Входные параметры: (url; функция, которая будет вызвана по окончании запроса; параметр этой функции)
function GetXMLdata(url, func, param) {
    httpRequest.open('GET', url, false, web_login, web_passw);
    httpRequest.setRequestHeader('User-Agent', app_name + ' ' + SVKTrans.version);
    httpRequest.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState == 4) {
            if (httpRequest.status == 401) {
                WriteLog('!Ответ сервера: 401 Unauthorized');
            } else {
                func(httpRequest, param);
            }
        }
    };
    try {
        httpRequest.send(null);
    } catch(e) {
        if (!/Method=\w*List/.test(url)) { // При отсутствии списка файлов на сервере возникает ошибка - это нормально (по мнению ЦБ). Не будем ее показывать, дабы не пугать пользователя
            // Все остальные ошибки - показываются:
            WriteLog('!Ошибка при открытии ' + url);
            online = false;
            StartTimer(3); // повтор неудавшегося подключения
            return;
        }
    }
    StartTimer(0); // периодическая проверка
    StartTimer(1); // проверка наличия исходящих сообщений
    if (num_timer_onfinish == 2) {
        StartTimer(2); // запрос ответа
    }
}

// ------------------------------------------------
// Обработка ответа сервера на запрос удаления одного файла
function FileDelete(response, race) {
    if (response.status != 200) {
        WriteLog('!Ошибка при удалении ' + race.filename + ': ' + response.statusText);
    }
}

// ------------------------------------------------
// Загрузка фильтров для сортировки входящих информационных сообщений
function LoadStatInfoInFilters() {
    for (var i = 0; i < 10; i++) {
        var value = INI.Paths['stat_info_in_filter' + i];
        if (value) {
            var params = /(.+\\)(.+)/.exec(value);
            if (params) {
                var masks = params[2].split('|');
                for (var j = 0; j < masks.length; j++) {
                    masks[j] = new RegExp('^' + masks[j].replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i');
                }
                StatInfoInFilters[StatInfoInFilters.length] = {
                    path: params[1],
                    re: masks
                };
            }
        }
    }
}

// ------------------------------------------------
// Возвращает индекс каталога назначения для входящего информационного сообщения или -1 для общего
function GetStatInfoIndex(filename) {
    for (var i = 0; i < StatInfoInFilters.length; i++) {
        for (var j = 0; j < StatInfoInFilters[i].re.length; j++) {
            if (StatInfoInFilters[i].re[j].test(filename)) {
                return i; // StatInfoInFilters[i].path;
            }
        }
    }
    return -1; // INI.Paths.stat_info_in + '\\';
}

// ------------------------------------------------
// Возвращает каталог назначения для входящего информационного сообщения
function GetStatInfoDest(filename) {
    for (var i = 0; i < StatInfoInFilters.length; i++) {
        for (var j = 0; j < StatInfoInFilters[i].re.length; j++) {
            if (StatInfoInFilters[i].re[j].test(filename)) {
                return StatInfoInFilters[i].path;
            }
        }
    }
    return INI.Paths.stat_info_in + '\\';
}

// ------------------------------------------------
// Сортировка принятых файлов по каталогам назначения в зависимости от формата сообщения
function SortingFiles(filename, format) {
    //if (isContainArray(['XMLEPD', 'XMLESID', 'RECEIPT'], format)) {
    if (isContainArray(['RECEIPT'], format)) {
        xmlDoc.load (filename);
        if (xmlDoc.parseError.errorCode) {
            WriteLog('!не читаемый ' + format + ' документ');
            return;
        }
    }
    // ------------------------------------------------
    switch (format) {
        /*
        case 'XMLEPD':
        case 'XMLESID':
            // Извлекаем из документа все адреса получателей в массив Recipients
            var To_arr = xmlDoc.getElementsByTagName('props:MessageInfo')[0].getElementsByTagName('props:To');
            var Recipients = [];  // массив, хранящий адреса получателей сообщения
            for (var i = 0; i < To_arr.length; i++) {
                Recipients.push(To_arr[i].childNodes[0].text);
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_work)) { // Логический адрес АРМ КБР
                WriteLog('это ' + format + ' документ для АРМ КБР');
                if (INI.Paths.pay_in) {
                    XCopyFile(filename, INI.Paths.pay_in + '\\', true); // в каталог входящих сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_ies)) { // Логический адрес АРМ КБР (для информационных сообщений)
                WriteLog('это ИНФОРМАЦИОННОЕ ' + format + ' сообщение для АРМ КБР');
                if (INI.Paths.pay_in) {
                    XCopyFile(filename, INI.Paths.pay_in + '\\', true); // в каталог входящих сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_test)) { // Логический тестовый адрес АРМ КБР
                WriteLog('это ТЕСТОВЫЙ ' + format + ' документ АРМ КБР');
                if (INI.Paths.pay_test_in) {
                    XCopyFile(filename, INI.Paths.pay_test_in + '\\', true); // Каталог входящих тестовых сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих тестовых сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_ies_test)) { // Логический тестовый адрес АРМ КБР (для информационных сообщений)
                WriteLog('это ТЕСТОВОЕ ИНФОРМАЦИОННОЕ ' + format + ' сообщение для АРМ КБР');
                if (INI.Paths.pay_test_in) {
                    XCopyFile(filename, INI.Paths.pay_test_in + '\\', true); // Каталог входящих тестовых сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих тестовых сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_51x)) { // Логический адрес АРМ КБР
                WriteLog('это ' + format + ' документ для АРМ КБР');
                if (INI.Paths.pay_in) {
                    XCopyFile(filename, INI.Paths.pay_in + '\\', true); // в каталог входящих сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            if (isContainArray(Recipients, INI.Account.UEO_uic_51x_test)) { // Логический тестовый адрес АРМ КБР
                WriteLog('это ТЕСТОВЫЙ ' + format + ' документ АРМ КБР');
                if (INI.Paths.pay_test_in) {
                    XCopyFile(filename, INI.Paths.pay_test_in + '\\', true); // Каталог входящих тестовых сообщений для АРМ КБР
                } else {
                    WriteLog('!Каталог входящих тестовых сообщений для АРМ КБР НЕ ЗАДАН');
                }
                return;
            }

            // Ни рабочий ни тестовый адрес нашего АРМ КБР в списке получателей не указан
            WriteLog('!этот ' + format + ' документ адресован НЕ НАМ');
            break;
        */
        case 'RECEIPT':
            var receipt_info = xmlDoc.getElementsByTagName('props:AcknowledgementInfo')[0].getElementsByTagName('props:ResultText')[0].childNodes[0].text;
            var fname = receipt_info.match(/имя файла:\s*([^\s,]+)/);
            receipt_info = fname ?
                fname[1] :
                ': "' + receipt_info + '"';

            switch(xmlDoc.getElementsByTagName('props:MessageInfo')[0].getElementsByTagName('props:To')[0].childNodes[0].text) {
                /*
                case INI.Account.UEO_uic_work:        // Логический адрес АРМ КБР
                    WriteLog('это квитанция о получении платежного документа ' + receipt_info);
                    if (INI.Paths.pay_kvit) {
                        XCopyFile(filename, INI.Paths.pay_kvit + '\\', true); // в каталог квитанций на сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                case INI.Account.UEO_uic_test: // Логический тестовый адрес АРМ КБР
                    WriteLog('это квитанция о получении ТЕСТОВОГО платежного документа ' + receipt_info);
                    if (INI.Paths.pay_test_kvit) {
                        XCopyFile(filename, INI.Paths.pay_test_kvit + '\\', true); // в каталог квитанций на тестовые сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на тестовые сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                case INI.Account.UEO_uic_ies: // Логический адрес АРМ КБР
                    WriteLog('это квитанция о получении ИНФОРМАЦИОННОГО сообщения ' + receipt_info);
                    if (INI.Paths.pay_kvit) {
                        XCopyFile(filename, INI.Paths.pay_kvit + '\\', true); // в каталог квитанций на сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                case INI.Account.UEO_uic_ies_test: // Логический тестовый адрес АРМ КБР
                    WriteLog('это квитанция о получении ТЕСТОВОГО ИНФОРМАЦИОННОГО сообщения ' + receipt_info);
                    if (INI.Paths.pay_test_kvit) {
                        XCopyFile(filename, INI.Paths.pay_test_kvit + '\\', true); // в каталог квитанций на тестовые сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на тестовые сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                case INI.Account.UEO_uic_51x: // Логический адрес АРМ КБР
                    WriteLog('это квитанция о получении ИНФОРМАЦИОННОГО сообщения ' + receipt_info);
                    if (INI.Paths.pay_kvit) {
                        XCopyFile(filename, INI.Paths.pay_kvit + '\\', true); // в каталог квитанций на сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                case INI.Account.UEO_uic_51x_test: // Логический тестовый адрес АРМ КБР
                    WriteLog('это квитанция о получении ТЕСТОВОГО ИНФОРМАЦИОННОГО сообщения ' + receipt_info);
                    if (INI.Paths.pay_test_kvit) {
                        XCopyFile(filename, INI.Paths.pay_test_kvit + '\\', true); // в каталог квитанций на тестовые сообщения для АРМ КБР
                    } else {
                        WriteLog('!Каталог квитанций на тестовые сообщения для АРМ КБР НЕ ЗАДАН');
                    }
                    break;
                */
                case INI.Account.from_addr: // Адрес отправителя файлов статистической отчетности
                    WriteLog('это квитанция о получении информационного сообщения ' + receipt_info);
                    if (INI.Paths.stat_info_kvit) {
                        XCopyFile(filename, INI.Paths.stat_info_kvit + '\\', true); // в каталог квитанций на информационные и сообщения из статистики
                    } else {
                        WriteLog('!Каталог квитанций на сообщения в статистику НЕ ЗАДАН');
                    }
                    break;
                    
                default:
                    WriteLog('это квитанция о получении НЕИЗВЕСТНОГО документа ' + receipt_info);
            }
            break;
        case 'INFO':
            WriteLog('это документ ПТК ПСД');
            if (INI.Paths.stat_info_in) {

                //var stat_info_in_dest = GetStatInfoDest(FSO.GetFileName(filename));
                //XCopyFile(filename, stat_info_in_dest + '\\', true); // в каталоги входящих информационных и сообщений из статистики

                var idx = GetStatInfoIndex(FSO.GetFileName(filename));
                if (idx == -1) {
                    XCopyFile(filename, INI.Paths.stat_info_in + '\\', true); // в общий каталог входящих информационных и сообщений из статистики
                } else {
                    XCopyFile(filename, INI.Paths.stat_info_in + '\\', true); // в общий каталог входящих информационных и сообщений из статистики   
                    XCopyFile(filename, StatInfoInFilters[idx].path + '\\', true); // в спецкаталог входящих информационных и сообщений из статистики   
                }

            } else {
                WriteLog('!Каталог входящих информационных и сообщений из статистики НЕ ЗАДАН');
            }
            break;
        default:
            WriteLog('!документ НЕ ОБРАБОТАН. Неизвестный MessageFormat: ' + format);
    }
}

// Обработка ответа сервера на запрос загрузки сегмента файла или одного файла
function FileDownload(response, race) {
    // Проверка размера загруженных сегментов
    function CheckSize(race) {
        if (race.segment_number == race.segments_count) {
            return race.Stream.Size == race.size;
        } else {
            return race.Stream.Size == race.segment_number * INI.Config.segment_size_download;
        }
    }

    if ((response.status == 200) ||
        (response.status == 204)) {
        race.Stream.Write(response.responseBody);
        if (CheckSize(race)) {
            if (race.segments_count > 1) {
                WriteLog(race.segment_number + ' сегмент загружен');
            }
        } else {
            race.Stream.Close();
            WriteLog('!Ошибка проверки размера файла ' + race.filename);
            return;
        }

        if ((race.segments_count == 1) ||
            (race.segment_number == race.segments_count) ||
            (response.status == 204)) {
            WriteLog(race.filename + ' загружен целиком');
            var filespec = store_path + race.filename;
            race.Stream.SaveToFile (filespec, 2); // adSaveCreateOverWrite
            race.Stream.Close();
            var file_hash = MD5(filespec);
            if (race.hash != file_hash) {
                WriteLog('!Ошибка проверки хеша файла ' + race.filename);
                FSO.DeleteFile(filespec, true);
                return;
            }
            WriteLog(race.filename + '@ сохранен. MD5 хеш проверен');
            if (INI.Paths.backup) {
                Backup(filespec, 'IN');
            }
            SortingFiles(filespec, race.format);
            WriteLog('Удаление с сервера ' + race.filename);
            GetXMLdata(url_get + '?Method=Delete&InstanceID=' + race.id, FileDelete, race);
            num_timer_onfinish = 0;
            InitTimer(2);
        }
    } else {
        WriteLog('!Ошибка загрузки ' + race.segment_number + ' сегмента файла ' + race.filename + ': ' + response.statusText);
    }
}

// Обработка ответа сервера на запрос XML списка рейсов
function GetList(response) {
    // ------------------------------------------------
    // Проверка, был загружен файл ранее или нет (по записи "filename.ext@" в сегодняшнем лог-файле)
    function isFileDownloaded(filename) {
        var dt = objDate();
        var logfilename = store_path + dt.year + dt.month + dt.day + ".log";
        var re = new RegExp(filename + '@', 'ig');
        if ((FSO.FileExists(logfilename)) &&
            (FSO.GetFile(logfilename).Size > 0)) {
            var tfile = FSO.OpenTextFile(logfilename);
            while (!tfile.AtEndOfStream) {
                var line = tfile.ReadLine();
                if (re.test(line)) {
                    tfile.Close();
                    return true;
                }
            }
            tfile.Close();
        }
        return false;
    }
    // ------------------------------------------------
    // Разбор содержимого файлового списка list.xml
    function ParseXML(filename) {
        // ------------------------------------------------
        function GetNodeText(node, tagName) {
            if (node.getElementsByTagName(tagName)[0]) {
                return node.getElementsByTagName(tagName)[0].childNodes[0].text;
            }
        }
        // ------------------------------------------------
        function DateUTC2Local(d) {
            var a = /(\d{4})\-(\d{2})\-(\d{2})T(\d{2}):(\d{2}):[\d\.]+Z/.exec(d);
            var dt = objDate(Date.UTC(a[1], a[2] - 1, a[3], a[4], a[5]));
            return dt.day + '.' + dt.month + '.' + dt.year + ' ' + dt.hours + ':' + dt.min;
        }
        // ------------------------------------------------
        var files = [];
        xmlDoc.load (filename);

        // Исправление известной ошибки ЦБ (когда в заголовке list.xml указана кодировка "utf-8", а текст содержит символы в win1251)
        if (xmlDoc.parseError.errorCode == -1072896760) {
            var str = ReadTextFile(filename);
            var re = /encoding\s*=\s*"utf-8"/i;
            if (re.test(str)) {
                WriteLog('!Кодировка символов в листинге не соответствует указанной! Пытаемся исправить...');
                str = str.replace(re, 'encoding="windows-1251"');
                WriteTextFile(filename, str);
                xmlDoc.load (filename);
            }
        }

        if (xmlDoc.parseError.errorCode) {
            WriteLog('!Загружен нечитабельный листинг ' + filename);
            return files;
        }

        var filelist = '';
        var nodes = new Enumerator(xmlDoc.getElementsByTagName('msglist:MessageItem'));
        for (; !nodes.atEnd(); nodes.moveNext()) {
            var messageItem = nodes.item();
            var file = {
                id:       GetNodeText(messageItem, 'msglist:InstanceID'),
                time:     DateUTC2Local(GetNodeText(messageItem, 'msglist:ReceiveTime')),
                format:   GetNodeText(messageItem, 'msglist:MessageFormat'),
                priority: GetNodeText(messageItem, 'msglist:Priority'),
                filename: GetNodeText(messageItem, 'msglist:LegacyTransportFileName'),
                size:     GetNodeText(messageItem, 'msglist:MessageSize'),
                hash:     GetNodeText(messageItem, 'msglist:MessageHash')
            };
            if (file.format == 'RECEIPT') {
                file.filename = file.id + '.RECEIPT';
            }
            files.push(file);
            filelist += '\n\t' + file.time + '\t' + file.format + '\t' + file.filename + '\t' + file.size;
        }

        if (files.length !== 0) {
            WriteLog('Имеются файлы для загрузки:' + filelist);
        }
        return files;
    }

    // ------------------------------------------------
    switch(response.status) {
        case 200:
            var xml_text = response.responseText;
            if (xml_text.length > 0) {
                var tmp_file = store_path + 'list.xml';
                WriteTextFile(tmp_file, xml_text);

                var races = ParseXML(tmp_file);
                if (races.length === 0) {
                    WriteLog('Нет файлов для загрузки');
                } else {
                    for (var i = 0; i < races.length; i++) {
                        var filename = races[i].filename;
                        if (filename && online) {
                            if ((INI.Config) && 
                                (INI.Config.file_ignore) && 
                                (new RegExp('^'+INI.Config.file_ignore.replace(/\./g,'\\.').replace(/\?/g,'.').replace(/\*/g,'.*?')+'$', "i")).test(filename)) {
                                WriteLog(' !Файл ' + filename + ' определен как ненужный');
                                WriteLog('Удаление с сервера ' + filename);
                                GetXMLdata(url_get + '?Method=Delete&InstanceID=' + races[i].id, FileDelete, races[i]);
                            } else {
                                if (isFileDownloaded(filename)) {
                                    //WriteLog(' !Файл ' + filename + ' был загружен ранее');
                                    //WriteLog('Удаление с сервера ' + filename);
                                    //GetXMLdata(url_get + '?Method=Delete&InstanceID=' + races[i].id, FileDelete, races[i]);
                                    var t = 2;
                                    while (BackupedIN(filename + '.' + t)) {
                                        t++;
                                    }
                                    filename += '.' + t;
                                    races[i].filename = filename;
                                }
                                //} else {
                                races[i].segments_count = 1;
                                if ((INI.Config) &&
                                    (INI.Config.segment_size_download)) {
                                    races[i].segments_count = Math.ceil(races[i].size / INI.Config.segment_size_download);
                                }
                                // Открываем Stream. В него будем грузить сегменты (или содержимое файла целиком)
                                races[i].Stream = new ActiveXObject('ADODB.Stream');
                                races[i].Stream.Type = 1; //adTypeBinary
                                races[i].Stream.Open();
                                // -----------------------------------------------------
                                if (races[i].segments_count > 1) { // Загрузка файла по сегментам
                                    WriteLog(' > Загрузка ' + filename + ' (' + races[i].size + ' байт. ' + races[i].segments_count + ' сегментов)');
                                    for (var n = 1; n <= races[i].segments_count; n++) {
                                        // Превентивный реконнект авторизации при длительных закачках
                                        if ((n % 100) === 0) {
                                            if (INI.Account.telnet_host) {
                                                TelnetConnect();
                                            }
                                        }
                                        var start = INI.Config.segment_size_download * (n - 1);
                                        var url_get_seg = url_get +
                                            '?Method=Download&InstanceID=' + races[i].id +
                                            '&Start=' + start +
                                            '&Count=' + INI.Config.segment_size_download;
                                        races[i].segment_number = n;
                                        GetXMLdata(url_get_seg, FileDownload, races[i]);
                                    }
                                // -----------------------------------------------------
                                } else { // Загрузка файла одним куском
                                    WriteLog(' > Загрузка ' + filename + ' (' + races[i].size + ' байт)');
                                    races[i].segment_number = 1;
                                    GetXMLdata(url_get + '?Method=Download&InstanceID=' + races[i].id, FileDownload, races[i]);
                                }
                            }
                        }
                    }
                }
            } else {
                // '200 ОК' но responseText ничего не вернул (бывает и такое)
                WriteLog('Ответ сервера на запрос списка: ' +
                    response.status + ' ' + response.statusText + ' (Возможно нет файлов для загрузки)');
            }
            break;
        // ------------------------------------------------
        case 400:
        case 405:
            WriteLog('!Ошибка при запросе расширенного листинга.');
            break;
        // ------------------------------------------------
        default:
            // При отсутствии списка сервер может вернуть 1223, 202 (возможны и другие варианты...)
            WriteLog('Ответ сервера на запрос списка: ' +
                response.status + ' ' + response.statusText + ' (Вероятно нет файлов для загрузки)');
    }
}

// Запрос списка рейсов на сервере
function Download(/*queue*/) {
    WshShell.RegWrite ('HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\GlobalUserOffline', 0, 'REG_DWORD');

    /*
    switch(queue) {
        case 1:
            if (INI.Account.web_login) {
                WriteLog('> Запрос списка платежных сообщений');
                web_login = INI.Account.web_login;
                web_passw = INI.Account.web_passw;
                GetXMLdata(url_get_list, GetList);
            }
            break;

        case 2:
    */
            if (INI.Account.web_login_info) {
                WriteLog('> Запрос списка информационных и сообщений из статистики');
                web_login = INI.Account.web_login_info;
                web_passw = INI.Account.web_passw_info;
                GetXMLdata(url_get_list, GetList);
            }

            if (INI.Account.web_login_test) {
                WriteLog('> Запрос списка тестовых платежных сообщений');
                web_login = INI.Account.web_login_test;
                web_passw = INI.Account.web_passw_test;
                GetXMLdata(url_get_list, GetList);
            }
    /*
            break;
    }
    */
}


//=====================================================
//  U P L O A D
//=====================================================

// Отправка одного файла (последовательность для IE: open, setRequestHeader, onreadystatechange, send)
function UploadFile(file, out_dir, data, segment_number, segments_count, guid) {
    httpRequest.open('POST', url_post, false, web_login, web_passw);
    //------------------------------------------------
    var filename = file.Name;
    var filename64 = utf8base64(filename);
    var filepath = file.Path;
    //------------------------------------------------
    // Общие заголовки
    httpRequest.setRequestHeader('User-Agent', app_name + ' ' + SVKTrans.version); // не обязателен
    httpRequest.setRequestHeader('Ack-Request', INI.Account.AckRequest); // для получения квитанций (только для бинарной статистики)
    // Для платежных xml сообщений заголовок 'Ack-Request' игнорируется, т.к. ЦОИ анализирует нод 'AckRequest' в теле xml сообщения формируемый АРМ КБР,
    // поэтому, если необходимы квитки, настройте АРМ КБР (Конфигурация - Транспортные реквизиты - Запрашивать квитанции).
    // В случае передачи любого некорректного сообщения реквест с отказом от обработки придет независимо от наличия/отсутствия данного заголовка
    httpRequest.setRequestHeader('If-None-Match', filename64);
    httpRequest.setRequestHeader('Content-length', file.Size); // не обязателен

    switch(out_dir) {
        case INI.Paths.stat_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', INI.Account.to_addr); // Адрес получателя файлов статистической отчетности
            break;
        case INI.Paths.expl_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'expl'); // Служба эксплуатации
            break;
        case INI.Paths.lic_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'lic'); // Управление лицензирования деятельности кредитных организаций
            break;
        case INI.Paths.otzi_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'otzi'); // Отдел по защите платежных технологий
            break;
        case INI.Paths.sopr_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'sopr'); // Отдел сопровождения учетно-операционной системы 
            break;
        case INI.Paths.ubn_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'ubn'); // Управление по надзору за деятельностью коммерческих банков 
            break;
        case INI.Paths.upsir_info_out:
            // Заголовки для информационных сообщений и статистики
            httpRequest.setRequestHeader('Content-Type', 'application/info');
            httpRequest.setRequestHeader('LegacyTransport-FileName', filename64);
            httpRequest.setRequestHeader('To', 'upsir'); // Рассылка документов
            break;
        /*
        case INI.Paths.pay_out:
            // Заголовки для платежек
            httpRequest.setRequestHeader('Content-Type','application/xmlepd');
            // httpRequest.setRequestHeader('To', 'uic:220200199800'); // Пользователь для обмена с ЦОИ (на этот адрес мы отсылаем платежки)
            // для платежных xml сообщений заголовок 'To' игнорируется, т.к. ЦОИ анализирует одноименный нод в теле xml сообщения
            break;
        case INI.Paths.pay_test_out:
            // Заголовки для тестовых платежек
            httpRequest.setRequestHeader('Content-Type','application/xmlepd');
            // httpRequest.setRequestHeader('To', 'uic:220200199811'); // Пользователь для тестового обмена с ЦОИ (на этот адрес мы отсылаем тесты)
            // для платежных xml сообщений заголовок 'To' игнорируется, т.к. ЦОИ анализирует одноименный нод в теле xml сообщения
            break;
        */
    }

    if (segment_number) { // Дополнительные заголовки для сегментированных файлов
        httpRequest.setRequestHeader('Segmentation-MessageID', guid);
        httpRequest.setRequestHeader('Segmentation-Number', segment_number);
        httpRequest.setRequestHeader('Segmentation-Count', segments_count);
    }

    // Обработка ответа сервера
    httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState == 4) {
            WriteLog('Ответ сервера: ' + httpRequest.status + ' ' + httpRequest.statusText);
            switch(httpRequest.status) {
                case 202:
                    switch(httpRequest.statusText) {
                        case 'Segment accepted':
                            WriteLog(segment_number + '-й сегмент из ' + segments_count + ' файла ' + filename + ' успешно отправлен');
                            // Превентивный реконнект авторизации при длительных отдачах
                            if ((segment_number % 100) === 0) {
                                if (INI.Account.telnet_host) {
                                    TelnetConnect();
                                }
                            }
                            UploadFile(file, out_dir, data, segment_number + 1, segments_count, guid);
                            return;
                        case 'Message accepted':
                            WriteLog('файл ' + filename + ' успешно отправлен целиком');
                            break;
                    }
                    break;
                case 400:
                    Restart();
                    return;
                case 412:
                    WriteLog('Файл ' + filename + ' уже был отправлен ранее. Удаляем его.');
                    break;
                default: // другой ответ сервера (считаем что файл не отправлен)
                    StartTimer(0); // периодическая проверка
                    return;
            }

            if (INI.Paths.backup) Backup(filepath, 'OUT');
            FSO.DeleteFile(filepath, true);
            StartTimer(2); // запрос ответа на отправленное сообщение
            StartTimer(0); // периодическая проверка
            StartTimer(1); // проверка наличия исходящих сообщений

        }
    };

    // теперь - сам запрос
    try {
        if (segment_number) {
            httpRequest.send(data[segment_number - 1]);
        } else {
            httpRequest.send(data[0]);
        }
    } catch(err) {
        WriteLog('!Ошибка при открытии ' + url_post);
        online = false;
        StartTimer(3); // повтор неудавшегося подключения
    }
}

// Проверка папок с файлами на отправку
function Upload(/*queue*/) {
    function EnumerateFiles(dir) {
        if (FSO.FolderExists(dir)) {
            var folder = FSO.GetFolder(dir);
            var files = new Enumerator(folder.Files);
            for (; !files.atEnd(); files.moveNext()) {
                if (online) {
                    var file = files.item();
                    var file_data = ReadFileSegments(file.Path);
                    var segments_count = file_data.length;
                    if (segments_count > 1) { // Отправка файла сегментами
                        var guid = GUID();
                        var segments_start = 1;
                        WriteLog(' > Отправка файла ' + file.Name + ' (' + file.Size + ' байт. ' + segments_count + ' сегментов ' + guid + ')');
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        //#2.9.7:
                        // Если отправка какого-нибудь большого файла срывается, используйте докачку:
                        // заполните переменные ниже своими значениями и временно снимите комментарий
                        // со следующего условного блока
/*
                        if (file.Name == 'ТК_КО_ezi06702.030.xml') { // имя проблемного файла
                            guid = 'c6a94a08-153b-bfbf-76af-48a7ebe6c249'; // взять из лога
                            segments_start = 577; // следующий номер сегмента, после того, на котором зависло
                        }
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        UploadFile(file, dir, file_data, segments_start, segments_count, guid);
                    } else { // Отправка файла одним куском
                        WriteLog(' > Отправка файла ' + file.Name + ' (' + file.Size + ' байт)');
                        UploadFile(file, dir, file_data);
                    }
                }
            }
        } else {
            WriteLog('!Каталог "' + dir + '" отсутствует');
        }
    }

    /*
    switch(queue) {
        case 1:
            // Исходящие платежные сообщения
            web_login = INI.Account.web_login;
            web_passw = INI.Account.web_passw;
            if ((INI.Paths.pay_out) &&
                (FSO.GetFolder(INI.Paths.pay_out).Files.Count > 0)) {
                // WriteLog(' > Отправка платежных сообщений:');
                EnumerateFiles(INI.Paths.pay_out);
            }
            break;
        case 2:
    */
            // Исходящие информационные и сообщения в статистику
            if (INI.Account.web_login_info) {
                web_login = INI.Account.web_login_info;
                web_passw = INI.Account.web_passw_info;
            }
            if ((INI.Paths.stat_info_out) &&
                (FSO.GetFolder(INI.Paths.stat_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.stat_info_out);
            }
            if ((INI.Paths.expl_info_out) &&
                (FSO.GetFolder(INI.Paths.expl_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.expl_info_out);
            }
            if ((INI.Paths.lic_info_out) &&
                (FSO.GetFolder(INI.Paths.lic_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.lic_info_out);
            }
            if ((INI.Paths.otzi_info_out) &&
                (FSO.GetFolder(INI.Paths.otzi_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.otzi_info_out);
            }
            if ((INI.Paths.sopr_info_out) &&
                (FSO.GetFolder(INI.Paths.sopr_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.sopr_info_out);
            }
            if ((INI.Paths.ubn_info_out) &&
                (FSO.GetFolder(INI.Paths.ubn_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.ubn_info_out);
            }
            if ((INI.Paths.upsir_info_out) &&
                (FSO.GetFolder(INI.Paths.upsir_info_out).Files.Count > 0)) {
                // WriteLog(' > Отправка документов ПТК ПСД:');
                EnumerateFiles(INI.Paths.upsir_info_out);
            }

            /*
            // Исходящие тестовые платежные сообщения
            if (INI.Account.web_login_test) {
                web_login = INI.Account.web_login_test;
                web_passw = INI.Account.web_passw_test;
            }
            if ((INI.Paths.pay_test_out) &&
                (FSO.GetFolder(INI.Paths.pay_test_out).Files.Count > 0)) {
                // WriteLog(' > Отправка ТЕСТОВЫХ платежных документов:');
                EnumerateFiles(INI.Paths.pay_test_out);
            }
            */
    /*
            break;
    }
    */
}


//=====================================================//
// F O R M   O N L O A D
//=====================================================//

// Инициализация объектов ActiveX
function ActiveX_Init() {
    var msxml_alert = 'Продолжение работы невозможно!\nУстановите MSXML 4.0 или более поздней версии.';

    function XMLHTTP_Init() {
        var progID = ['Msxml2.ServerXMLHTTP.6.0', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Microsoft.XMLHTTP'];
        for (var i = 0; i < progID.length; i++) {
            try {
                httpRequest = new ActiveXObject(progID[i]);
                if (i > 3) {
                    alert('Отправка файлов статотчетности работать не будет!\nУстановите более позднюю версию MSXML.');
                } else if (i < 1) {
                    var Timeout_Resolve = 50 * 1000;
                    var Timeout_Connect = 50 * 1000;
                    var Timeout_Send = 150 * 1000;
                    var Timeout_Receive = 150 * 1000;
                    httpRequest.setTimeouts(Timeout_Resolve, Timeout_Connect, Timeout_Send, Timeout_Receive);
                }
                return httpRequest;
            }
            catch (e)
            {
            }
        }
        alert(msxml_alert);
        self.close();
    }

    function XMLDOC_Init() {
        var vers = ['.6.0', '.5.0', '.4.0', ''];
        for (var i = 0; i < vers.length; i++) {
            try {
                xmlDoc = new ActiveXObject('Msxml2.DOMDocument' + vers[i]);
                return xmlDoc;
            }
            catch (e)
            {
            }
        }
        alert(msxml_alert);
        self.close();
    }

    try {
        WshShell = new ActiveXObject('WScript.Shell');
        FSO = new ActiveXObject('Scripting.FileSystemObject');
    }
    catch(e) {
        alert('Продолжение работы невозможно!\nУстановите Windows Script Host.');
        self.close();
    }

    try {
        adoStream = new ActiveXObject('ADODB.Stream');
    }
    catch(e) {
        alert('Продолжение работы невозможно!\nОтсутсвует ADODB.Stream');
        self.close();
    }

    XMLHTTP_Init();
    XMLDOC_Init();
}

// Регистрация и инициализация XStandard Buffer ActiveX (http://www.xstandard.com/en/documentation/xbuffer/)
function InitXBuffer() {
    try {
        objXBuffer = new ActiveXObject('XStandard.Buffer');
    }
    catch(e) {
        var title = 'Установка XStandard Buffer ActiveX';
        var buffer = script_path + 'XBuffer.dll';
        if (WshShell.Popup('Для контроля загруженных файлов по MD5 хешу необходимо зарегистрировать XStandard Buffer ActiveX\nЗапустить процедуру регистрации?', 0, title, 36) != 6) {
            self.close();
        }
        if (!FSO.FileExists(buffer)) {
            WshShell.Popup('Файл XBuffer.dll в текущем каталоге\nО Т С У Т С Т В У Е Т !', 5, title, 48);
            self.close();
        }
        WshShell.Run('regsvr32 "' + buffer + '"', 1, true);
        try {
            objXBuffer = new ActiveXObject('XStandard.Buffer');
        }
        catch(e) {
            WshShell.Popup('Ошибка при регистрации XBuffer.dll!', 5, title, 16);
            self.close();
        }
    }
}

// Регистрация и инициализация Socket ActiveX
function InitSocket() {
    if (!INI.Account.telnet_host) {
        return;
    }
    try {
        objSocket = new ActiveXObject('Socket.TCP');
    }
    catch(e) {
        var title = 'Установка w3Sockets ActiveX';
        var socket = script_path + 'Socket.dll';
        if (WshShell.Popup('Для telnet аутентификации необходимо зарегистрировать w3Sockets ActiveX\nЗапустить процедуру регистрации?', 0, title, 36) != 6) {
            self.close();
        }
        if (!FSO.FileExists(socket)) {
            WshShell.Popup('Файл Socket.dll в текущем каталоге\nО Т С У Т С Т В У Е Т !', 5, title, 48);
            self.close();
        }
        WshShell.Run('regsvr32 "' + socket + '"', 1, true);
        try {
            objSocket = new ActiveXObject('Socket.TCP');
        }
        catch(e) {
            WshShell.Popup('Ошибка при регистрации Socket.dll!', 5, title, 16);
            self.close();
        }
    }
}

// Загрузка ограничений по дате/времени в массив Arr_Restrictions
function LoadRestrictions() {
    if ((!FSO.FileExists(rest_file)) ||
        (FSO.GetFile(rest_file).Size === 0)) {
        return;
    }
    var tfile = FSO.OpenTextFile(rest_file);
    while (!tfile.AtEndOfStream) {
        var line = tfile.ReadLine();
        if ((!/^\s*$/.test(line)) && (!/^\s*;/.test(line))) {
            Arr_Restrictions[Arr_Restrictions.length] = line.replace(/;.*$/,'').replace(/\s*$/,'');
        }
    }
    tfile.Close();
}

// Действия при загрузке формы
function On_Load() {
    LoadRestrictions();
    timeout[0] = INI.Timeout.next;
    timeout[1] = INI.Timeout.outbox;
    timeout[2] = INI.Timeout.conform;
    timeout[3] = INI.Timeout.connerror;

    ReadLog(); // загружаем предыдущие записи из лога
    InitAllTimers();
    StartTimer(1); // проверка наличия исходящих сообщений
    ShowTimers();
    CheckRestrictions();
    StartTimer(0); // периодическая проверка
    WriteLog(' > === ' + document.title + '. Старт программы === <');
}

// Обработка клика на чекбоксе "отключить периодическую проверку в заданные дни и часы."
function ChkRestrictionsOnClick() {
    if (TextRestrict.style.color == 'red') {
        if (!ChkRestrictions.checked) {
            StartTimer(0); // периодическая проверка
        } else {
            InitTimer(0);
        }
    }
}

</script>

<script type="text/vbscript">
Function ASCIIChar(code)
    ASCIIChar = Chr(code)
End Function
</script>

</head>
<body onload="On_Load()">
<table width="100%" height="100%" cellpadding="0" cellspacing="0" border="0">
    <tr><td>
        <fieldset><legend>&nbsp;Время, оставшееся до...</legend>
            <table width="100%" cellpadding="0" cellspacing="0" border="0">
                <tr>
                    <td width="100%">&nbsp;&nbsp;... периодической проверки входящих сообщений:&nbsp;<span style="color:highlight;font-weight:bold;">&#185;</span>
                    <td align="center"><span style="font:normal normal 16pt Wingdings; color:highlight;">&#xC1;</span>&nbsp;
                    <td><input type=text id=TimerVisible disabled><input type=text id=TimerHidden>&nbsp;
                <tr>
                    <td width="100%">&nbsp;&nbsp;... проверки наличия исходящих сообщений:&nbsp;
                    <td align="center"><span style="font:normal normal 12pt Wingdings; color:highlight;">&#x2B;</span>&nbsp;
                    <td><input type=text id=TimerVisible disabled><input type=text id=TimerHidden>&nbsp;
                <tr>
                    <td width="100%">&nbsp;&nbsp;... запроса ответа на отправленное сообщение:&nbsp;
                    <td align="center"><span style="font:normal normal 12pt Wingdings; color:highlight;">&#x2E;</span>&nbsp;
                    <td><input type=text id=TimerVisible disabled><input type=text id=TimerHidden>&nbsp;
                <tr>
                    <td width="100%">&nbsp;&nbsp;... повтора неудавшейся попытки соединения:&nbsp;
                    <td align="center"><span style="font:normal normal 16pt Wingdings; color:highlight;">&#x29;</span>&nbsp;
                    <td><input type=text id=TimerVisible disabled><input type=text id=TimerHidden>&nbsp;
            </table>
        </fieldset>
    <tr><td>
        <input id="ChkRestrictions" type=checkbox checked onClick="ChkRestrictionsOnClick()">
        <label hidefocus for="ChkRestrictions"><span style="font-weight:bold;">&#185;</span>&nbsp;отключить периодическую проверку в <a href="#" onClick="WshShell.Run('notepad ' + rest_file)" title="Открыть файл ограничений по датам и времени" hidefocus>заданные</a> дни и часы. Текущее ограничение: <span id="TextRestrict"></span></label>
    <tr height="30px"><td align="center">
        <button onClick="num_timer_onfinish=4; Connect()" hidefocus>Установить соединение немедленно</button>
    <tr height="100%"><td>
        <textarea style="width:100%; height:100%;" id=idLog></textarea>
</table>
</body>
</html>